/*
	This file is part of AVRCppLib.

    AVRCppLib is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AVRCppLib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AVRCppLib.  If not, see <http://www.gnu.org/licenses/>.

    Copyright (C) Maxime Lemonnier 2007-2011
 */

#include "I2CMaster.h"
#include <stdio.h>
#include "../../Utils/Utils.h"
#include "../../Utils/Delay.h"

namespace Coms
{

namespace I2C
{

#define WAIT_TIME 8000

I2CMaster::I2CMaster(I2CHardware * i2cHardware) :
i2cHardware(i2cHardware)
{
	// TODO Auto-generated constructor stub

}

I2CMaster::~I2CMaster()
{
	// TODO Auto-generated destructor stub
}

void I2CMaster::setDestinationAddress(uint8_t destinationAddress)
{
	this->destinationAddress = destinationAddress;
}

bool I2CMaster::selectRegister(uint8_t registerAddress)
{
	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);

//	#if defined(DEBUG) fprintf(stderr, PROG_MEM_STRING("sending STOP_SIGNAL\n\r"));

	i2cHardware->command(TransmitActions::START_SIGNAL);
	while(!i2cHardware->readInterruptFlag());
//		#if defined(DEBUG) fprintf(stderr, PROG_MEM_STRING("waiting START_SIGNAL\n\r"));

	if(i2cHardware->getStatus() != TW_START)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_START\n\r"));
		#endif
		return false;
	}

	i2cHardware->writeDestinationAddress(destinationAddress, TransmitDirections::WRITE);
	i2cHardware->command(TransmitActions::READY);
	while(!i2cHardware->readInterruptFlag());

	if(i2cHardware->getStatus() != TW_MT_SLA_ACK)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_MT_SLA_ACK\n\r"));
		#endif
		return false;
	}

	i2cHardware->writeByte(registerAddress);
	i2cHardware->command(TransmitActions::READY);
	while(!i2cHardware->readInterruptFlag());

	if(i2cHardware->getStatus() != TW_MT_DATA_ACK)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_MT_DATA_ACK\n\r"));
		#endif
		return false;
	}

	return true;
}

uint8_t I2CMaster::readRegister(uint8_t registerAddress)
{
	uint8_t registerContent;

	if(!selectRegister(registerAddress))
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("selectRegister() failed, could not contact slave\n\r"));
		#endif
		return 0;
	}

	i2cHardware->command(TransmitActions::START_SIGNAL);
	while(!i2cHardware->readInterruptFlag());

	if(i2cHardware->getStatus() != TW_REP_START)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_REP_START\n\r"));
		#endif
			return 0;
	}

	i2cHardware->writeDestinationAddress(destinationAddress, TransmitDirections::READ);
	i2cHardware->command(TransmitActions::READY);
	while(!i2cHardware->readInterruptFlag());

	if(i2cHardware->getStatus() != TW_MR_SLA_ACK)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_MR_SLA_ACK\n\r"));
		#endif
		return 0;
	}

	// SLA+R ACK'ed : here we do nothing

	i2cHardware->command(TransmitActions::READY); // DATA_NACK will be returned after the first (and last byte have been received)
	while(!i2cHardware->readInterruptFlag());

	if(i2cHardware->getStatus() != TW_MR_DATA_NACK)
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("i2cHardware->getStatus() != TW_MR_DATA_NACK\n\r"));
		#endif
		return 0;
	}

	registerContent = i2cHardware->readByte();

	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);


	return registerContent;

}

void I2CMaster::writeRegister(uint8_t registerAddress, uint8_t registerContent)
{

	while(!selectRegister(registerAddress))
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("selectRegister() failed, could not contact slave\n\r"));
		#endif
		return;
	}

	i2cHardware->writeByte(registerContent);

	i2cHardware->command(TransmitActions::READY);
	while(!i2cHardware->readInterruptFlag());

	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);

}

void I2CMaster::writeRegistersAM(uint8_t registerAddress, uint8_t *RegisterBuf, uint32_t BufSizeByte){

	while(!selectRegister(registerAddress))
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("selectRegister() failed, could not contact slave\n\r"));
		#endif

		return;
	}

	for(unsigned int i = 0; i < BufSizeByte; i++){
		i2cHardware->writeByte(RegisterBuf[i]);
		i2cHardware->command(TransmitActions::READY);
		while(!i2cHardware->readInterruptFlag());
	}

	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);


}

void I2CMaster::readRegisters(uint8_t startRegisterAddress, ActiveReceiveBuffer * receiveBuffer)
{
	if(!selectRegister(startRegisterAddress))
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("selectRegister() failed, could not contact slave\n\r"));
		#endif

		return;
	}

	i2cHardware->command(TransmitActions::START_SIGNAL);
	while(!i2cHardware->readInterruptFlag());

	i2cHardware->writeDestinationAddress(destinationAddress, TransmitDirections::READ);

	i2cHardware->command(TransmitActions::READY);
	while(!i2cHardware->readInterruptFlag());

	while(receiveBuffer->isOpen())
	{

		i2cHardware->command(TransmitActions::ACKNOWLEDGE); // DATA_ACK will be returned

		while(!i2cHardware->readInterruptFlag());

		receiveBuffer->receiveByte(i2cHardware->readByte());
	}

	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);

}

void I2CMaster::writeRegisters(uint8_t startRegisterAddress, ActiveTransmitBuffer * transmitBuffer)
{

	if(!selectRegister(startRegisterAddress))
	{
		#if defined(DEBUG)
			fprintf(stderr, PROG_MEM_STRING("selectRegister() failed, could not contact slave\n\r"));
		#endif
		return;
	}

	while(transmitBuffer->isOpen())
	{
		i2cHardware->writeByte(transmitBuffer->transmitByte());

		i2cHardware->command(TransmitActions::READY);
		while(!i2cHardware->readInterruptFlag());
	}

	i2cHardware->command(TransmitActions::STOP_SIGNAL);
	wasteCycles(WAIT_TIME);

}

}

}
